﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Windows.Media.Imaging;

namespace GameEngine
{
    public delegate void ViewportChangedHandler(object sender, ViewportChangedArgs e);

    /// <summary>
    ///     Arguments for the Viewport Changed event.
    /// </summary>
    public class ViewportChangedArgs : EventArgs
    {
        private IntSize _size;
        private IntPoint _center;
        public ViewportChangedArgs(IntSize size, IntPoint center)
        {
            this._size = size;
            this._center = center;
        }

        public IntSize Size
        {
            get
            {
                return _size;
            }
        }

        public IntPoint Center
        {
            get
            {
                return _center;
            }
        }
    }


    /// <summary>
    ///     The classes which can serve as a Viewport center.
    /// </summary>
    public enum CenterTo
    {
        tile, 
        gameObject
    }

    /*
     * to be reworked with viewport mask
    /// <summary>
    ///     Shapes of the viewport supported.
    /// </summary>
    public enum ViewportShape
    {
        rectangle,
        ellipse
    }
     * */

    /// <remarks>
    ///     Represents a viewport for gamegrids.
    /// </remarks>
    public class Viewport
    {
        /*
         * to be reworked with viewport mask
        /// <summary>
        ///     Holds the information whether the Viewport was changed.
        /// </summary>
        private bool _changed = true;
        /// <summary>
        ///     Gets/Sets the information whether the Viewport was changed.
        /// </summary>
        public bool Changed
        {
            get
            {
                return _changed;
            }
            set
            {
                _changed = value;
            }
        }
        */

        /// <summary>
        ///     Position of the viewport centre tile in tiles.
        /// </summary>
        private IntPoint _centerTile { get; set; }
        /// <summary>
        ///     Center of viewport-GameObject.
        /// </summary>
        private AnimatedGameObject _centerGameObject { get; set; }
        /// <summary>
        ///     Whether the center is GameObject or Tile.
        /// </summary>
        private CenterTo _centerTarget { get; set; }
        /// <summary>
        ///     Size of the viewport in pixels.   
        /// </summary>
        public IntSize _size{ get; set;}
        /// <summary>
        ///     Center of the viewport in pixels.  
        /// </summary>
        private IntPoint _center { get; set; }

        /*
         * to be reworked with viewport mask
        /// <summary>
        ///     Shape of the viewport.
        /// </summary>
        public ViewportShape _shape { get; set; }

        /// <summary>
        ///     The bitmap representing the viewport.
        /// </summary>
        private WriteableBitmap _viewport;
         * */



        public void GetViewportDimensions(IntSize boardSize, out IntPoint start, out IntPoint end, out IntPoint offset)
        {
            start = new IntPoint();
            end = new IntPoint();
            offset = new IntPoint();

            //start.X = this._center.X - (int)(Math.Floor((double)this._size.Width / 2));
            start.X = this._center.X - (this._size.Width / 2);
            /*if (start.X < 0)
                start.X = 0;
            else if (start.X > boardSize.Width)
                start.X = boardSize.Width;*/

            //start.Y = this._center.Y - (int)(Math.Floor((double)this._size.Height / 2));
            start.Y = this._center.Y - (this._size.Height / 2);
            /*if (start.Y < 0)
                start.Y = 0;
            else if (start.Y > boardSize.Height)
                start.Y = boardSize.Height;*/

            //end.X = this._center.X + (int)(Math.Ceiling((double)this._size.Width / 2));
            end.X = this._center.X + (this._size.Width / 2);
            //correction
            if (this._size.Width % 2 != 0)
            {
                end.X++;
            }

            /*if (end.X > boardSize.Width)
                end.X = boardSize.Width;
            else if (end.X < 0)
                end.X = 0;*/

            //end.Y = this._center.Y + (int)(Math.Ceiling((double)this._size.Height / 2));
            end.Y = this._center.Y + (this._size.Height / 2);
            //correction
            if (this._size.Height % 2 != 0)
            {
                end.Y++;
            }
            /*if (end.Y > boardSize.Height)
                end.Y = boardSize.Height;
            else if (end.Y < 0)
                end.Y = 0;*/

            //offfset is the negative part of the start position
            if (start.X < 0)
            {
                offset.X = -start.X;
                start.X = 0;
            }
            //pokus
            else
            {
                offset.X = -(start.X % 32);
                start.X += offset.X;
            }
            //pokus
            /*if (end.X > boardSize.Width)
            {
                offset.X = -(end.X - boardSize.Width);
                end.X = boardSize.Width;
            }*/

            if (start.Y < 0)
            {
                offset.Y = -start.Y;
                start.Y = 0;
            }
                //pokus
            else
            {
                offset.Y = -(start.Y % 32);
                start.Y += offset.Y;
            }
            //pokus
            /*if (end.Y > boardSize.Height)
            {
                offset.Y = -(end.Y - boardSize.Height);
                end.Y = boardSize.Height;
            }*/
        }


        /// <summary>
        ///     Creates a viewport centered on Tile<paramref name="centerTile"/>.
        /// </summary>
        /// <param name="centerTile">Coordinates of the center tile.</param>
        /// <param name="size">Size of the viewport.</param>
        public Viewport(IntPoint centerTile, IntSize tileSize, IntSize viewportSize)
        {
            _centerTile = centerTile;
            _centerTarget = CenterTo.tile;
            _center = new IntPoint(_centerTile.X * tileSize.Width, _centerTile.Y * tileSize.Height);
            _size = viewportSize;
        }


        /// <summary>
        ///     Creates a viewport centered on GameObject<paramref name="centerGameObject"/>.
        /// </summary>
        /// <param name="centerGameObject">The center AnimatedGameObject, which must be existent in the current board
        /// (the main character for instance).
        /// </param>
        /// <param name="size">Size of the viewport.</param>
        public Viewport(AnimatedGameObject centerGameObject, IntSize viewportSize)
        {
            _centerGameObject = centerGameObject;
            _centerTarget = CenterTo.gameObject;
            _size = viewportSize;
            _center = new IntPoint(_centerGameObject.Position.X, _centerGameObject.Position.Y);

            _centerGameObject.PositionChanged += new PositionChangedHandler(CenterPositionChanged);
        }


        /// <summary>
        ///     When the position of the object we center to changes, we change the viewport center.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CenterPositionChanged(object sender, PositionChangedArgs e)
        {
            _center = new IntPoint(_centerGameObject.Position.X, _centerGameObject.Position.Y);

            OnViewportChanged(new ViewportChangedArgs(_size, _center));
        }


        /// <summary>
        ///     Fires when the Viewport changes.
        /// </summary>
        public event ViewportChangedHandler ViewportChanged;
        protected virtual void OnViewportChanged(ViewportChangedArgs e)
        {
            if (ViewportChanged != null)
                ViewportChanged(this, e);
        }


        /*
         *  to be reworked with viewport mask
        /// <summary>
        ///     Returns the bitmap which represents the viewport with black background and transparent foreground.
        /// </summary>
        /// <param name="tileSize">Size of one tile from the gameboard.</param>
        /// <returns>Bitmap representing the viewport.</returns>
        public WriteableBitmap GetViewport(IntSize tileSize)
        {
            if ( (_size != null) && (_size.Height != 0) && (_size.Width != 0) )
            {
                //creates new bitmap
                _viewport = new WriteableBitmap((int)(_size.Width)*tileSize.Width, (int)(_size.Height)*tileSize.Height);
                //clears it to black
                _viewport.Clear(Color.FromArgb(0, 255, 255, 255));
                //creates the transparent brush for the shape
                SolidColorBrush brush = new SolidColorBrush(Color.FromArgb(100, 0, 0, 0));
                //sets shape of the viewport
                Shape viewportShape = null;
                if (_shape == ViewportShape.ellipse)
                {
                    viewportShape = new Ellipse();
                }
                else if (_shape == ViewportShape.rectangle)
                {
                    viewportShape = new Rectangle();
                }
                //sets the width and height
                viewportShape.Height = _size.Height;
                viewportShape.Width = _size.Width;
                //sets the transparent fill
                viewportShape.Fill = brush;
                //without stroke
                viewportShape.Stroke = null;
                //and renders the shape to bitmap
                _viewport.Render(viewportShape, null);
                _viewport.Invalidate();

                _changed = false;
                return _viewport;
            }
            else
                return null;
        }
        */
    }
}
